﻿// //  ********************************************************************************
// //  Copyright (C) 2016 Sebastian Kenter. All rights reserved.
// // 
// //  Name    : PhotonServerProject | PhotonServerFramework | ServerApplication.cs
// //  Created : 2016 - 03 - 21 11:05
// //  Modified: 2016 - 04 - 18 09:37
// //  ********************************************************************************

using System;
using System.IO;
using Autofac;
using Autofac.Configuration;
using ExitGames.Logging;
using ExitGames.Logging.Log4Net;
using log4net;
using log4net.Config;
using Photon.SocketServer;
using Photon.SocketServer.ServerToServer;
using PhotonServerFramework.Implementation.Configuration;
using PhotonServerFramework.Interfaces.Application;
using PhotonServerFramework.Interfaces.Client;
using PhotonServerFramework.Interfaces.Server;
using LogManager = ExitGames.Logging.LogManager;

namespace PhotonServerFramework.Implementation.Application
{
    /// <summary>
    ///     Central application class that get's instantiated by photon control for each server instance configured in
    ///     PhotonServer.config.
    ///     From this class we create the IServerApplications, register AutoFac modules and create the peers.
    /// </summary>
    public class ServerApplication : ApplicationBase
    {
        /// <summary>
        ///     Unique server id that gets transferred to the corresponding IServerApplication
        /// </summary>
        public Guid ServerId { get; } = Guid.NewGuid();

        /// <summary>
        ///     Central logger instance
        /// </summary>
        private ILogger Logger { get; set; }

        /// <summary>
        ///     Factory class for creating client peers and incoming sub server peers
        /// </summary>
        private IClientPeerFactory ClientPeerFactory { get; set; }

        /// <summary>
        ///     Factory class to create outgoing server peers (in response to connection requests)
        /// </summary>
        private IServerPeerFactory ServerPeerFactory { get; set; }

        /// <summary>
        ///     Server module class that gets created alongside with the photon applications
        /// </summary>
        private IServerApplication Server { get; set; }

        /// <summary>
        ///     Creates an incoming client peer or an incoming sub server peer depending on the local port the request is
        ///     sent to (if MasterServerTcpPort then it's a sub server, if ClientUdpPort it's a client)
        /// </summary>
        /// <param name="initRequest">The initial request send by the ConnectToServerTCP (sub server) or Connect (client)</param>
        /// <returns>IClientPeer or IServerPeer implementation depending on the request</returns>
        protected override PeerBase CreatePeer(InitRequest initRequest)
        {
            Logger.InfoFormat(
                "Received init request (local: {0}:{1}, remote: {2}:{3}, connection: {4}, protocol: {5}, type= {6})",
                initRequest.LocalIP, initRequest.LocalPort, initRequest.RemoteIP, initRequest.RemotePort,
                initRequest.ConnectionId, initRequest.Protocol.ProtocolType,
                initRequest.PhotonPeer.GetPeerType().ToString());
            PeerConfiguration config = new PeerConfiguration();
            config.AddConfiguration(initRequest);
            return ClientPeerFactory.CreateClientPeer<PeerBase>(config);
        }

        /// <summary>
        ///     Creates a server peer in response to the initial request sent by ConnectToServerTCP (by a sub server)
        /// </summary>
        /// <param name="initResponse">The initial response from the server tried to connect to</param>
        /// <param name="state">A state object</param>
        /// <returns>IServerPeer implementation</returns>
        protected override ServerPeerBase CreateServerPeer(InitResponse initResponse, object state)
        {
            Logger.InfoFormat(
                "Received init response (local: {0}:{1}, remote: {2}:{3}, connection: {4}, protocol: {5}, type= {6})",
                initResponse.LocalIP, initResponse.LocalPort, initResponse.RemoteIP, initResponse.RemotePort,
                initResponse.ConnectionId, initResponse.Protocol.ProtocolType,
                initResponse.PhotonPeer.GetPeerType().ToString());
            PeerConfiguration config = new PeerConfiguration();
            config.AddConfiguration(initResponse);
            return ServerPeerFactory.CreateServerPeer<ServerPeerBase>(config);
        }

        protected override void Setup()
        {
            InitializeLogger();
            InitializeAutofac();
            Server.Setup();
        }

        /// <summary>
        ///     Gets called when a stop of the server is requested by photon control
        /// </summary>
        protected override void OnStopRequested()
        {
            Server.OnStopRequested();
            base.OnStopRequested();
        }

        /// <summary>
        ///     This is the final stage for cleanup before the instance gets finally disposed by photon control
        /// </summary>
        protected override void TearDown()
        {
            Server.TearDown();
        }

        /// <summary>
        ///     Gets called when the connection to a server fails
        /// </summary>
        /// <param name="errorCode">The photon error code</param>
        /// <param name="errorMessage">The photon error message</param>
        /// <param name="state">The photon state object (<see cref="P:Photon.SocketServer.ApplicationBase" /></param>
        protected override void OnServerConnectionFailed(int errorCode, string errorMessage, object state)
        {
            Server.OnServerConnectionFailed(errorCode, errorMessage, state);
        }

        /// <summary>
        ///     Initializes the logger instance used by the application
        /// </summary>
        private void InitializeLogger()
        {
            LogManager.SetLoggerFactory(Log4NetLoggerFactory.Instance);
            GlobalContext.Properties["LogName"] = ApplicationName;
            GlobalContext.Properties["LogBasePath"] = BinaryPath;
            GlobalContext.Properties["LogPath"] = ApplicationName;

            // Set the log4net configuration file
            XmlConfigurator.ConfigureAndWatch(new FileInfo(Path.Combine(BinaryPath, "log4net.config")));

            Logger = LogManager.GetLogger(ApplicationName);
        }

        /// <summary>
        ///     Initializes Autofac, registers and resolves the central components of the application
        /// </summary>
        private void InitializeAutofac()
        {
            ContainerBuilder builder = new ContainerBuilder();

            builder.RegisterInstance(this).As<ServerApplication>().SingleInstance();
            builder.RegisterInstance(Logger).As<ILogger>();
            builder.RegisterType<IClientPeerFactory>();
            builder.RegisterType<IServerPeerFactory>();
            builder.RegisterType<IServerApplication>();

            // Register a module for the application that creates the IServerApplication
            builder.RegisterModule(
                new XmlFileReader(Path.Combine(BinaryPath, "Modules") + "\\" + ApplicationName + ".config"));


            IContainer container = builder.Build();

            // Resolving from a scope is better than directly from the container
            using (ILifetimeScope scope = container.BeginLifetimeScope())
            {
                Logger = scope.Resolve<ILogger>();
                ClientPeerFactory = scope.Resolve<IClientPeerFactory>();
                ServerPeerFactory = scope.Resolve<IServerPeerFactory>();
                Server = scope.Resolve<IServerApplication>();
            }
        }
    }
}
